package com.youlai.netty.service.tcp;


import java.io.IOException;
import java.net.InetSocketAddress;

import cn.hutool.core.util.RandomUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;


/**
 * @author zc
 * @date 2023-01-30 22:12
 */
@Slf4j
@ChannelHandler.Sharable
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{

    /**
     *  注册时执行
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info("客户端:{}注册成功",ctx.channel().id().toString());
//        ctx.channel().writeAndFlush("客户端:"+ctx.channel().id().asLongText()+"注册成功");
    }

    /**
     *  离线时执行
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
        log.info("客户端离线:{}",ctx.channel().id().toString());
        ctx.channel().isOpen();
    }

    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            if(msg == null){return;}
            MyMessageProtocol data = (MyMessageProtocol) msg;
            String channelId = ctx.channel().id().toString();
            log.info("handlerl处理:{}消息:{}",channelId,data);
            ctx.writeAndFlush("server:"+ RandomUtil.randomNumbers(10));
        } catch (Exception e) {
            log.error("读取客户端:{}数据异常:{}",ctx.channel().id().toString(),e.getMessage());
        }finally {
            try {
                log.info("传递给下一个handler");
                super.channelRead(ctx,msg);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 从客户端收到新的数据、读取完成时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
       // log.info("读取客户端:{}数据完成",ctx.channel().id().toString());
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
        log.error("处理客户端:{}数据异常:{}",ctx.channel().id().toString(),cause.getMessage());
        cause.printStackTrace();
        BootNettyChannel bootNettyChannel = BootNettyChannelCache.get("server:"+ctx.channel().id().toString());
        if(bootNettyChannel != null){
            BootNettyChannelCache.remove("server:"+ctx.channel().id().toString());
        }
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        ctx.channel().read();
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
       log.info("客户端:{}连接成功",ctx.channel().id().toString());
    }

    /**
     * 客户端与服务端 断连时 执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        log.info("客户端:{}断开连接",ctx.channel().id().toString());
        BootNettyChannel bootNettyChannel = BootNettyChannelCache.get("server:"+ctx.channel().id().toString());
        if(bootNettyChannel != null){
            BootNettyChannelCache.remove("server:"+ctx.channel().id().toString());
        }
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
    }

    /**
     * 服务端当read超时, 会调用这个方法
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException {
        super.userEventTriggered(ctx, evt);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        log.info("读取客户端:{}读取数据超时",ctx.channel().id().toString());
        ctx.close();//超时时断开连接
    }

}
